package camid.main.jmeter;

import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import camid.util.ssh.SSHCommandProcessor;

public class CamidJmeterElasticAgent
{

	private static final Logger logger = LogManager.getLogger(CamidJmeterElasticAgent.class.getName());
	
	private static CamidJmeterElasticAgent INSTANCE;

	private boolean started;
	private int counter;
	
	public static synchronized CamidJmeterElasticAgent getInstance()
	{
		if(INSTANCE == null)
		{
			INSTANCE = new CamidJmeterElasticAgent();
		}
		return INSTANCE;
	}

	private String threadGroupString;

	private Long waitInterval;

	private String mainHostname;

	private List<String> workerHostnames;

	private SSHCommandProcessor processor;

	private ExecutorService executor;

	private List<Thread> testThreads;

	private Lock sshWorkersLock;

	private CamidJmeterElasticAgent()
	{
		counter = 0;
		EnviromentVariableHandler envHandler = EnviromentVariableHandler.getInstance();

		this.waitInterval = 110000L;
		this.mainHostname = envHandler.getMainNode();
		this.workerHostnames = envHandler.getWorkNodes();

		String user = envHandler.getUser();
		File key = envHandler.getKey();

		this.testThreads = new LinkedList <Thread>();

		//		this.processor = new SSHCommandProcessor(user, key);

		this.executor = Executors.newCachedThreadPool();

		this.sshWorkersLock = new ReentrantLock();
	}

//	public void setWaitInterval(long waitInterval)
//	{
//		synchronized(testThreads)
//		{
//			if(testThreads.isEmpty())
//			{
//				this.waitInterval = waitInterval;
//			}
//		}
//	}

	public void startMain()
	{
		try {
			System.out.println("Starting main node...");
			EnviromentVariableHandler env = EnviromentVariableHandler.getInstance();
			//					String result = processor.startCamidMainNodeCommand(mainHostname, 22, 15001);
			Process p = Runtime.getRuntime().exec(String.format(
					"bash %s -host %s -key %s -main",
					env.getStartnodeScript().getAbsoluteFile(),
					mainHostname, 
					env.getKey().getAbsoluteFile() ));

			p.waitFor();

			System.out.println("Main node started!");

		}
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}



	public Future<?> startWorkers()
	{
		String workerHost = workerHostnames.get(0);
		startWorker(workerHost, mainHostname);
		Runnable job = new Runnable()
		{
			@Override
			public void run() 
			{
				List<String> workerHosts = workerHostnames;
				for(int i = 1; i < workerHosts.size() ; i++)
				{
					String workerHost = workerHosts.get(i);
					startWorker(workerHost, mainHostname);
					
					try {
						Thread.sleep(waitInterval);
					} catch (InterruptedException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}
			}

		};
		Future<?> fut = this.executor.submit(job);
		return fut;
	}
	
	private static void startWorker(String workerHost, String mainHostname)
	{
		try {
			EnviromentVariableHandler env = EnviromentVariableHandler.getInstance();
			//						String result = processor.startCamidMainNodeCommand(mainHostname, 22, 15001);
			Process p = Runtime.getRuntime().exec(String.format(
					"bash %s -host %s -key %s -nshost %s",
					env.getStartnodeScript().getAbsoluteFile(),
					workerHost, 
					env.getKey().getAbsoluteFile(),
					mainHostname));

			p.waitFor();

			System.out.println("Starting Worker Node " + workerHost);
			//						processor.startCamidWorkNodeCommand(workerHost, 22, 15001, mainHostname, 15001);
		} 
		catch (IOException e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		catch (InterruptedException e) 
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void stopMain()
	{
		try {
			//					processor.killJavaCommand(mainHostname, 22);
			EnviromentVariableHandler env = EnviromentVariableHandler.getInstance();
			Runtime rt = Runtime.getRuntime();
			
			String scriptPath = env.getStopnodeScript().getAbsolutePath();
			String keyPath = env.getKey().getAbsolutePath();
			
			String cmd = String.format(
					"bash %s -host %s -key %s",
					scriptPath,
					mainHostname, 
					keyPath);
			
			logger.info("Calling: " + cmd);
			Process p = rt.exec(cmd);

			p.waitFor();
			
			StringBuilder sb = new StringBuilder();
			Scanner scanner = new Scanner(p.getInputStream());
			while(scanner.hasNext())
			{
				sb.append(scanner.nextLine() + "\n");
			}
			
			String response = sb.toString();
			logger.info("Receiving" + response);
			

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
//		return fut;
	}


	public void stopWorkers()
	{
		for(String workerHost : workerHostnames)
		{

			try {

//				EnviromentVariableHandler handler = EnviromentVariableHandler.getInstance();
//				Runtime rt = Runtime.getRuntime();
//				Process p = rt.exec(String.format(
//						"ssh %s -i %s ubuntu@%s \"killall java\" ",
//						"-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no",
//						handler.getKey().getAbsolutePath(),
//						workerHost
//						));
				
				EnviromentVariableHandler env = EnviromentVariableHandler.getInstance();
				Runtime rt = Runtime.getRuntime();
				
				String scriptPath = env.getStopnodeScript().getAbsolutePath();
				String keyPath = env.getKey().getAbsolutePath();
				
				String cmd = String.format(
						"bash %s -host %s -key %s",
						env.getStopnodeScript().getAbsolutePath(),
						workerHost, 
						env.getKey().getAbsolutePath());
				
				logger.info("Calling: " + cmd);
				Process p = rt.exec(cmd);

				p.waitFor();
				
				StringBuilder sb = new StringBuilder();
				Scanner scanner = new Scanner(p.getInputStream());
				while(scanner.hasNext())
				{
					sb.append(scanner.nextLine() + "\n");
				}
				
				String response = sb.toString();
				logger.info("Receiving: " + response);
				
			} 
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

//	static final Pattern pattern = Pattern.compile("(TG\\x2d\\d{2,3}\\x2d\\d{3,4}).*");
//	static final Pattern pattern = Pattern.compile("(TG\\x2dMain).*");
//
//	private static String extractThreadGroup(String threadName)
//	{
//		String tg = null;
//		Matcher m = pattern.matcher(threadName);
//		m.find();
//		tg = m.group(1);
//
//		return tg;
//	}

	public void startBaseSystem()
	{
//		Thread t = Thread.currentThread();
//
//		String tName = t.getName();
//		String tg = extractThreadGroup(tName);

		
		try {
			this.sshWorkersLock.lock();
			
//			if(this.threadGroupString == null || !tg.equals(this.threadGroupString))
			if(!started)
			{
				
//				try {
					stopMain();
					stopWorkers();
//					this.threadGroupString = tg;
					startMain();
//				}
				
			}
			
		}
		catch(Throwable throwable)
		{
			
			throwable.printStackTrace();
		}
		finally
		{
			started = true;
			this.sshWorkersLock.unlock();
		}

	}
	
	public void startAllWorkers()
	{
		try {
			this.sshWorkersLock.lock();
			int val = this.counter++;
			if(val == 0)
			{
				startWorkers();
			}
		}
		catch(Throwable t)
		{
			t.printStackTrace();
		}
		finally
		{
			this.sshWorkersLock.unlock();
		}
	}
	
	public void shutdown()
	{
		
		try {
			this.sshWorkersLock.lock();
			int val = this.counter--;
			if(val == 1)
			{
				stopMain();
				stopWorkers();
				this.executor.shutdown();
			}
		}
		catch(Throwable t)
		{
			t.printStackTrace();
		}
		finally {
			this.sshWorkersLock.unlock();
		}
	}

//	private void startCamidProcesses() 
//	{
//		try {
//			startMain().get();
//			startWorkers();
//		} catch (InterruptedException e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		} catch (ExecutionException e1) {
//			// TODO Auto-generated catch block
//			e1.printStackTrace();
//		}
//
//
//	}

	/*
	public void scheduleThreadTermination(final Thread t)
	{
		Runnable terminationJob = new Runnable()
		{

			@Override
			public void run() {
				try
				{
					t.join();
					synchronized(testThreads)
					{
						testThreads.remove(t);
						testThreads.notify();
					}

				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		};

		executor.submit(terminationJob);
	}

	public void scheduleCamidTermination()
	{
		Runnable monitorJob = new Runnable()		
		{
			@Override
			public void run() 
			{
				try {
					Thread.sleep(120000);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

				int counter = 0;
				int size = 0;

				//				synchronized(testThreads)
				//				{
				//					size = testThreads.size();
				//				}

				//				while(counter < size)
				//				{
				//					Thread t = null;
				//					synchronized (testThreads) 
				//					{
				//						t = testThreads.get(counter);
				//						counter++;
				//					}
				//					
				//					try 
				//					{
				//						t.join();
				//						
				//					} 
				//					catch (InterruptedException e) 
				//					{
				//						// TODO Auto-generated catch block
				//						e.printStackTrace();
				//					}
				//					
				//					
				//					synchronized(testThreads)
				//					{
				//						size = testThreads.size();
				//					}
				//						
				//				}
				//				
				//				synchronized(testThreads)
				//				{
				//					for(Iterator<Thread> iter = testThreads.iterator(); iter.hasNext(); )
				//					{
				//						iter.next();
				//						iter.remove();
				//					}
				//				}

				synchronized(testThreads)
				{
					while(testThreads.size() > 0)
					{
						try 
						{
							testThreads.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}

				stopMain();
				stopWorkers();
			}

		};

		this.executor.submit(monitorJob);
	}
	*/

	
	public static void main(String[] args) {
		int x = 0;
		int val = x++;
		System.out.println(val);
		val = x--;
		System.out.println(val);
	}
}
